/******************************************************************************/
/* File: thread.h                                                             */
/******************************************************************************/
#include "thread.h"

/******************************************************************************/
/******************************************************************************/

Thread::Thread() : mTid(0), bIsDetached(false){
	pthread_attr_init(&mAttr);
}

Thread::~Thread() {
	if(IsRunning())
		throw ThreadException(ThreadException::errThreadKill);

	pthread_attr_destroy(&mAttr);
}

pthread_t Thread::Self() {
    return pthread_self();
}

pthread_t Thread::GetThreadID() {
	return mTid;
}

bool Thread::IsRunning() {
	return pthread_kill(mTid, 0) == 0 ? true : false;
}

void Thread::SetAttribute(enum Attribute a, void *value) {

	switch (a) {

		case StackSize :
			pthread_attr_setstacksize(&mAttr, (size_t)value);
			break;
		case Priority :
			pthread_attr_setschedparam(&mAttr, (const struct sched_param *)value);
			break;
		default:
			break;
	}
}

void Thread::GetAttribute(enum Attribute a, void *value) {

	switch (a) {

		case StackSize :
			pthread_attr_getstacksize(&mAttr, (size_t *)value);
			break;
		case Priority:
			pthread_attr_getschedparam(&mAttr, (struct sched_param *)value);
			break;
		default:
			break;
	}
}

void * Thread::exec(void *thr) {
	reinterpret_cast<Thread *> (thr)->Run();
}

void Thread::Start(void *arg) {
	this->arg = arg;

	int ret = pthread_create(&mTid, NULL, &Thread::exec, this);
	if(ret != 0)
		throw ThreadException(ThreadException::errThreadCreate);
}

void Thread::Join() {

	if(pthread_join(mTid, NULL) != 0)
		throw ThreadException(ThreadException::errThreadJoin);

}

void Thread::Detach() {

	if(pthread_detach(mTid) != 0) 
		throw ThreadException(ThreadException::errThreadDetach);

	bIsDetached = true;
}

void Thread::Cancel() {

	if(!IsRunning()) {
		mTid = 0;
		return;
	}

	if(pthread_cancel(mTid) != 0)
		throw ThreadException(ThreadException::errThreadCancel);

	if(bIsDetached)
		return;
	
	Join();
}

void Thread::Kill(int sig) {
	if(pthread_kill(mTid, sig) != 0)
		throw ThreadException(ThreadException::errThreadKill);
}

/******************************************************************************/
/******************************************************************************/

Mutex::Mutex() {
	int  r = pthread_mutex_init(&mMutex, 0);
	if (r != 0)
		throw ThreadException(ThreadException::errMutex);
}

Mutex::~Mutex() {
	int r = pthread_mutex_destroy(&mMutex);
	if (r != 0)
		throw ThreadException(ThreadException::errMutex);
}

int Mutex::Lock() {
	int r = pthread_mutex_lock(&mMutex);
	if(r != 0)
		return -1;
		
	return 0;
}

int Mutex::Unlock() {
	int r = pthread_mutex_unlock(&mMutex);
	if(r != 0)
		return -1;
		
	return 0;
}

/******************************************************************************/
/******************************************************************************/

CriticalSection::CriticalSection(Mutex *pMutex) {
	if(pMutex == 0)
		throw ThreadException(ThreadException::errMutex);
	
	mpMutex = pMutex;

	int r = mpMutex->Lock();
	if(r != 0)
		throw ThreadException(ThreadException::errMutex);
}

CriticalSection::~CriticalSection() {
	int r = mpMutex->Unlock();
	if(r != 0)
		throw ThreadException(ThreadException::errMutex);
}

/******************************************************************************/
/******************************************************************************/

ConditionVariable::ConditionVariable() {
	int r = pthread_cond_init(&mConditionVariable, 0);
	if (r != 0)
		throw ThreadException(ThreadException::errCondition);
}

ConditionVariable::~ConditionVariable() {
	int r = pthread_cond_destroy(&mConditionVariable);
	if (r != 0)
		throw ThreadException(ThreadException::errCondition);
}

int ConditionVariable::Wait(Mutex *pMutex) {
	if(pMutex == NULL)
		return -1;

	int r = pthread_cond_wait(&mConditionVariable, &(pMutex->mMutex));
	if (r != 0)
		return -1;
		
	return 0;
}

int ConditionVariable::Wakeup() {
	int r = pthread_cond_signal(&mConditionVariable);
	if (r != 0)
		return -1;
		
	return 0;
}

int ConditionVariable::WakeupAll() {
	int r = pthread_cond_broadcast(&mConditionVariable);
	if (r != 0)
		return -1;
	
	return 0;
}

/******************************************************************************/
/******************************************************************************/
Event::Event() {
	mFlag = 0;
	mbSemaphore = false;
}

Event::Event(bool bSemaphore) {
	mFlag = 0;
	mbSemaphore = bSemaphore;
}

Event::~Event() {
}

int Event::Set() {
	
	try {
		CriticalSection cs(&mMutex);
		mFlag++;
	} catch(...) {
		return -1;
	}

	int r = mCond.Wakeup();
	if(r != 0)
		return -1;

	return 0;
}

int Event::Wait() {
	
	try {
		CriticalSection cs(&mMutex);

		while(mFlag == 0) {
			int s = mCond.Wait(&mMutex);
			if(s != 0)
				return -1;
		}

		if(mbSemaphore) {
			mFlag--;
		}
		else {
			mFlag = 0;
		}
	}
	catch(...) {
		return -1;
	}
	
	return 0;
}

/******************************************************************************/
/******************************************************************************/

ThreadException::ThreadException(ErrorCodes ecType = errUnknown) 
			   : ecException(ecType){

	switch (ecException) {
		case errThreadCreate:
			SetString("Cannot create thread in context.\n");
			break;
		case errThreadJoin:
			SetString("Cannot join the thread.\n");
			break;
		case errThreadDetach:
			SetString("Detach thread failed.\n");
			break;
		case errThreadCancel:
			SetString("Cannot cancel the thread.\n");
			break;
		case errThreadKill:
			SetString("Cannot use signal to terminate thread.\n");
			break;
		case errMutex:
			SetString("Mutex operator error.\n");
			break;
		case errCondition:
			SetString("Condition operator error.\n");
			break;
		case errUnknown:
			SetString("An unknown error occured.\n");
			break;
		default:
			SetString("An unknown error occured.\n");
	}
}

ThreadException::ThreadException(const ThreadException &cOriginal) {
	ecException = cOriginal.ecException;
    SetString(cOriginal.pcExceptionString);
}

ThreadException::~ThreadException() {
    delete pcExceptionString;
}

void ThreadException::PrintException() {
    std::cout << "Thread Exception: " << pcExceptionString;
}

void ThreadException::SetString(const char *pcErrString) {
    try {
        pcExceptionString = new char[strlen(pcErrString) + 1];
        strcpy(pcExceptionString, pcErrString);
    }
    catch (...) {
        pcExceptionString = NULL;
    }
}

/******************************************************************************/
/* End File: thread.h                                                         */
/******************************************************************************/